/**
 * @Copyright ®2015 Sinosoft Co. Ltd. All rights reserved.<br/>
 * 项目名称 : 电子商务云服务平台
 * 创建日期 : 2016年6月28日
 * 修改历史 : 
 *     1. [2016年6月30日]修改文件 by chihouliang
 *      添加Javadoc注释，更新代码规范，静态工具类增加private构造函数，代码优化<br/>
 *      添加方法:<code>getUUID</code> <code>generateNo</code>
 */
package com.gavinwind.inspay.common.util;

import java.util.UUID;

/**
 * 序列号工具类，提供3中生成序列号的方式：生成UUID、随机数+时间戳、自定义组合随机序列号。生成指定位数，数字、大小写字母组合的字符串。
 * @date 2010-11-26
 * @since JDK 1.5
 * @author dingkui
 * @version 1.0
 * 
 */
public class SerialNoUtil {

    /*
     * 生成序列号工具类不能进行实例化
     */
    private SerialNoUtil(){}

    /**
     * 生成序列号的类型。1-数字序列号
     */
    public static final int CHAR_TYPE_NUMBER = 1;

    /**
     * 生成序列号的类型。2-小写字母序列号
     */
    public static final int CHAR_TYPE_LOWER_CASE_LETTER = 2;

    /**
     * 生成序列号的类型。3-大写字母序列号
     */
    public static final int CHAR_TYPE_UPPER_CASE_LETTER = 3;

    /**
     * 数字数组，生成序列号使用
     */
    private static final char[] NUMBER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

    /**
     * 小写字母数组，生成序列号使用
     */
    private static final char[] LOWER_CASE_CHAR_SET = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
            'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };

    /**
     *大写字母数组，生成序列号使用 
     */
    private static final char[] UPPER_CASE_CHAR_SET = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
            'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };

    /**
     * 
     * 根据指定位数，指定的集合，生成随机字符串 目前集合有三种，数字、大写字母和小写字母<br>
     * 用法实例<br>
     * <pre>
     *      int[] noType = {1,3};// 1-数字，2-小写字母，3-大写字母 。可以随意组合
     *      String no = SerialNoUtil.genetateRandomSerialNo(32,noType);
     * </pre>
     * @param cBit 要生成随机字符串的位数
     * @param cTypes 类型列表，1代表数字，2代表小写字母，3代表大写字母
     * @return 生成的指定位数、指定类型组合的随机字符串
     */
    public static String genetateRandomSerialNo(int cBit, int[] cTypes) {
        int tNewSetLength = 0;
        for (int i = 0; i < cTypes.length; ++i) {
            switch (cTypes[i]) {
            case CHAR_TYPE_NUMBER:
                tNewSetLength += NUMBER_SET.length;
                break;
            case CHAR_TYPE_LOWER_CASE_LETTER:
                tNewSetLength += LOWER_CASE_CHAR_SET.length;
                break;
            case CHAR_TYPE_UPPER_CASE_LETTER:
                tNewSetLength += UPPER_CASE_CHAR_SET.length;
                break;
            }
        }
        if (tNewSetLength <= 0) {
            return "";
        }
        char[] tNewSet = new char[tNewSetLength];
        int tTotalSetCount = 0;
        for (int i = 0; i < cTypes.length; ++i) {
            switch (cTypes[i]) {
            case CHAR_TYPE_NUMBER:
                tTotalSetCount = montage(tNewSet, NUMBER_SET, tTotalSetCount);
                break;
            case CHAR_TYPE_LOWER_CASE_LETTER:
                tTotalSetCount = montage(tNewSet, LOWER_CASE_CHAR_SET, tTotalSetCount);
                break;
            case CHAR_TYPE_UPPER_CASE_LETTER:
                tTotalSetCount = montage(tNewSet, UPPER_CASE_CHAR_SET, tTotalSetCount);
                break;
            }
        }
        StringBuffer tSb = new StringBuffer();
        for (int i = 0; i < cBit; ++i) {
            int tCurIndex = (int) (Math.random() * (double) tNewSetLength);
            tSb.append(tNewSet[tCurIndex]);
        }
        return tSb.toString();
    }
    
    /**
     * 
     * 获取32的UUID，大写字母+数字组成。UUID理论上是一个不会重复的全球唯一序列号
     * @return 生成的UUID，唯一序列号
     * @history 
     *   1 add  by chihouliang 2016年6月30日  生成UUID
     */
    public static String getUUID(){
        String tUuid=UUID.randomUUID().toString();
        tUuid = tUuid.replaceAll("-", "").toUpperCase();
        return tUuid;
    }
    
    /**
     * 生成16随机数字序列号，使用时间+3位随机数方式生成
     * @return 生成的序列号，数字组成唯一序列号。例如本地测试生成的16位随机数：1467167204619203
     * @history 
     *   1 add  by chihouliang 2016年6月30日  生成16位时间戳+随机数
     */
    public static String generateNo() {
        return generateNo(16);
    }
    
    /**
     * 生成指定位数的随机数字序列号，使用时间+位随机数方式生成。<br/>注：如果传入长度过长或者低于0，将使用随机数的最大值。大约29、30位（由于随机数不确定性）。<br/>如果长度小于13位，可能会重复（由于前面使用时间戳）
     * @param cLen 生成序列号的程度
     * @return 生成的数字序列号，数字组成唯一序列号。
     * @history 
     *   1 add  by chihouliang 2016年6月30日  生成指定位数数字序列号：时间戳+随机数
     */
    public static String generateNo(int cLen){
        StringBuffer tSb = new StringBuffer();
        String tTime = System.currentTimeMillis() + ""; // 时间
        String tRandomNum = Math.random() + ""; // 随机数
        String tRandomStr = tRandomNum.substring(2); // 去掉随机数前面的0.
        tSb.append(tTime).append(tRandomStr);
        
        if(cLen<0 ||tSb.length()<cLen){
            cLen = tSb.length();
        }
        
        return tSb.substring(0, cLen);
    }

    /*
     * 内部方法，拼接数组
     * @param cDest 目标数组
     * @param cOri 源数组
     * @param cDestLength 目标数组起始下标
     * @return 目标数组终止下标
     */
    private static int montage(char[] cDest, char[] cOri, int cDestLength) {
        for (int i = 0; i < cOri.length; ++i) {
            cDest[cDestLength + i] = cOri[i];
        }
        return cOri.length + cDestLength;
    }
}